/**
 * author:0283000106
 */
package com.zte.claa.inficombo.csa.app.service.gprscat1.mina;

import java.nio.charset.Charset;
import java.util.HashMap;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import org.json.JSONObject;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.BytesArrayForCC;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMessage;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMsgBody;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMsgHead;
import com.zte.claa.inficombo.csa.app.service.rptparse.CRC16M;
import com.zte.claa.inficombo.csa.app.util.EdianUtil;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;


public class GPRSDevMsgDecoder2 implements MessageDecoder {
	// logger
	private static final Logger LOG = LoggerFactory.getLogger(GPRSDevMsgDecoder2.class);
	// 字符集
	private Charset charset = null;

	public GPRSDevMsgDecoder2(Charset charset) {
		this.charset = charset;
		if (this.charset == null) {
			this.charset = Charset.forName("UTF-8");
		}
	}
	/*
	 * decodable()方法有三个返回值，分别表示如下的含义：
		A. MessageDecoderResult.NOT_OK：表示这个解码器不适合解码数据，然后检查其它解码
		器，如果都不满足会抛异常；
		B. MessageDecoderResult.NEED_DATA：表示当前的读入的数据不够判断是否能够使用这
		个解码器解码，然后再次调用decodable()方法检查其它解码器，如果都是NEED_DATA,
		则等待下次输入；
		C. MessageDecoderResult.OK： 表示这个解码器可以解码读入的数据， 然后则调用
		MessageDecoder 的decode()方法。
		这里注意decodable()方法对参数IoBuffer in 的任何操作在方法结束之后，都会复原，也就是
		你不必担心在调用decode()方法时，position 已经不在缓冲区的起始位置。这个方法相当于
		是预读取，用于判断是否是可用的解码器。
	 * */	
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		LOG.info("Received gprscat1 data from remoteAddr=["+session.getRemoteAddress().toString()+"],data= ["+in.getHexDump()+"]");
		int bufferlen = in.remaining();
		int msgtype = GPRSMsgHead.GPRSCAT1_INVALID_TYPE;
		try {
			byte[] tmpBs = new byte[bufferlen];
			for(int pos=0; pos<bufferlen; pos++){
				tmpBs[pos] = in.get();
				if(0 == pos && tmpBs[0] != '{'){					
					LOG.error("[1].Decodable Current data is not valid json,json format is invalid!...["+in.getHexDump()+"]");
					return MessageDecoderResult.OK;
				}				
				String jsonstr = new String(tmpBs);
				if(!JsonUtils.isJSONValid(jsonstr)){
					if((bufferlen-1) == pos){
						LOG.info("[1].Decodable Current data is not valid json, wait new data coming...["+in.getHexDump()+"]");
						return MessageDecoderResult.NEED_DATA;
					}
					continue;
				}else{					
					JSONObject jsonObject = new JSONObject(jsonstr);
					Object dtype = jsonObject.get("dt");
					if(null == dtype){
						LOG.error("[2].Decodable Can not get dt field value, jsonstr:["+jsonstr+"]");
						return MessageDecoderResult.OK;
					}
					msgtype = (int)dtype;
					if(GPRSMsgHead.GPRSCAT1_UP_INDEX != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_ALARM != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_CMDRSP != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_UPGDATAREQ != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_UPGPROG != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_REGREQ != msgtype &&
						GPRSMsgHead.GPRSCAT1_UP_HBREQ != msgtype
					){
						LOG.error("[3].Decodable Current data is not correct,dt:"+msgtype+",jsonstr:["+jsonstr+"]");
						return MessageDecoderResult.OK;
					}
					break;
				}
			}
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decodable error: "+e.getMessage(), e);
		}
		return MessageDecoderResult.OK;
	}
	
	/*
	 * decode()方法有三个返回值，分别表示如下的含义：
		A. MessageDecoderResult.NOT_OK：表示解码失败，会抛异常；
		B. MessageDecoderResult.NEED_DATA：表示数据不够，需要读到新的数据后，再次调用
		decode()方法。
		C. MessageDecoderResult.OK：表示解码成功。
	 * */
	public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		LOG.info("begin to decode gprscat1 data[sessionid=" + session.getId() + "]...["+in.getHexDump()+"]");
		try {
			GPRSMsgHead msgHead = new GPRSMsgHead();
			GPRSMsgBody msgBody = new GPRSMsgBody();
			GPRSMessage msg     = new GPRSMessage();
			int bufferlen = in.remaining();
			int msgtype = GPRSMsgHead.GPRSCAT1_INVALID_TYPE;
			int crccode = 0;
			byte[] tmpBs = new byte[bufferlen];
			int tmpBsIdx = 0;
			// 先找到起始的左大括号
			int validstartpos = Integer.MAX_VALUE;
			for(int i=0; i<bufferlen; i++){
				byte tmpbyte = in.get();
				if(tmpbyte == '{'){
					validstartpos = i;
					tmpBs[tmpBsIdx++] = tmpbyte;					
					break;
				}
			}
			
			for(int pos=validstartpos+1; pos<bufferlen; pos++){				
				byte tmpbyte = in.get();
				tmpBs[tmpBsIdx++] = tmpbyte;
				String jsonstr = new String(tmpBs);
				if(!JsonUtils.isJSONValid(jsonstr)){
//					if((bufferlen-1) == pos){
//						LOG.info("[2].Decode Current data is not valid json, jsonstr:["+jsonstr+"]");
//						return MessageDecoderResult.NEED_DATA;
//					}
					continue;
				}else{
					JSONObject jsonObject = new JSONObject(jsonstr);
					Object dtype = jsonObject.get("dt");
					if(null == dtype){					
						LOG.error("[3].Decode Can not get dt field value, jsonstr:["+jsonstr+"]");
						this.processInvalidMsg(session, in);
						return MessageDecoderResult.OK;
					}
					msgtype = (int)dtype;
					
					Object crc = jsonObject.get("crc");
					if(null == crc){
						LOG.error("[4].Decode Can not get crc field value, jsonstr:["+jsonstr+"]");
						this.processInvalidMsg(session, in);
						return MessageDecoderResult.OK;						
					}
				    crccode = (int)crc;
					JSONObject datajson = jsonObject.getJSONObject("data");
					if(null == datajson){						
						LOG.error("[5].Decode Can not get data field value, jsonstr:["+jsonstr+"]");
						this.processInvalidMsg(session, in);
						return MessageDecoderResult.OK;
					}					
					if(GPRSMsgHead.GPRSCAT1_UP_REGREQ == msgtype){
						String deveui = datajson.getString("deveui");
						String imei = datajson.getString("imei");
						msgHead.setMsgtype(msgtype);
						msgHead.setCrccode(crccode);
						msgHead.setDeveui(deveui);
						msgHead.setImei(imei);
						msgBody.setDatajson(datajson);
					}else if(GPRSMsgHead.GPRSCAT1_UP_HBREQ == msgtype ||
							GPRSMsgHead.GPRSCAT1_UP_INDEX == msgtype || 
							GPRSMsgHead.GPRSCAT1_UP_ALARM == msgtype){
						msgHead.setMsgtype(msgtype);
						msgHead.setCrccode(crccode);
						msgBody.setDatajson(datajson);
					}
					// crc编码校验
					boolean crcvalid = this.checkCrcValid(jsonstr, crccode);
					if(true != crcvalid){
						msg.setErrtype(GPRSMessage.GPRSCAT1_ERR_CRCCHECK);
						LOG.error("***[session=" + session.getId()+"]decode error,crc check is failed");
						//this.processInvalidMsg(session, in);
						return MessageDecoderResult.OK;
					}
					break;
				}
			}
			msg.setBody(msgBody);
			msg.setHead(msgHead);			
			// 返回解码结果
			out.write(msg);
			LOG.info("OK: finish gprscat1 decoding[session=" + session.getId()+", msg: "+msg.toString());
			return MessageDecoderResult.OK;
			
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decode error:"+e.getMessage(), e);
			this.processInvalidMsg(session, in);			
		}
		return MessageDecoderResult.OK;
	}
	

	private void processInvalidMsg(IoSession session, IoBuffer in){
		// in.getHexDump() 不会调整pos和limit等指针.
		LOG.warn("Buffer decode: gprscat1 data is invalid, pos="+in.position()+", limit="+in.limit()+", data="+in.getHexDump());
		int pos = in.position();
		int limit = in.limit();
		int leftSize = in.remaining();
		in.skip(leftSize);
		LOG.warn("***[session=" + session.getId()+"]skip invalid data:　pos="+pos+", limit="+limit+", leftSize="+leftSize+", left-data="+in.getHexDump());
	}


	public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
		LOG.debug("FinishDecode for session: " + session.getId());
	}
	
	private boolean checkCrcValid(String srcstr, int srccrc) {
		if(null == srcstr || srcstr.length() == 0){
			return false;
		}
		String matchstr = "\"data\":{";
		int matchlen = matchstr.length();
		int startidx = srcstr.indexOf(matchstr);
		int endidx = srcstr.indexOf("}",startidx+matchlen-1);
		String substr = srcstr.substring(startidx+matchlen-1,endidx+1);
		
		String hexstrtmp = "";
		for(int i=0; i<substr.length(); i++){
			char charstmp = substr.charAt(i);
            int charcode = Integer.valueOf(charstmp);
	        hexstrtmp += Integer.toHexString(charcode);
		}
		LOG.info("checkCrcValid srcstr data ascii str:"+hexstrtmp);		
		int crc16code = CRC16M.getCrc16CodeInt(hexstrtmp); 
		if(srccrc != crc16code){
			LOG.info("checkCrcValid targetcrc16:"+crc16code+",srccrc16code:"+srccrc);
			return false;
		}
		return true;
	}
	
}
